
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <stdbool.h>


#include "evalsoc.h"




extern int32_t uart_write( dw_uart_regs_t * puart, uint8_t val );
extern uint8_t uart_read( dw_uart_regs_t * puart );




int putchar( int ch )
{
	uart_write( UART2, (uint8_t)ch );
    return ch;
}


int puts( const char * ptr )
{
	int  i;
	
	for ( i=0; ; i++ )
	{
		if ( ptr[i] == '\0' )
		{
			break;
		}
		
		putchar( ptr[i] );
	}

    putchar( 0x0A );
	return i;
}


int getchar( void )
{
    return (int)(uint32_t)uart_read( UART2 );
}


#if 0

int getchar( void )
{
	arm_uart_regs_t * puart = (arm_uart_regs_t *)ARM_UART0_BASE;
	
    while ( puart->FR_bit.RXFE != 0 ) {
        __NOP();
    }
    
    /* todo : check error flag ...*/
    return (int)( puart->DR );
}

#endif


static int mini_itoa( int value, unsigned int radix, unsigned int uppercase, unsigned int unsig, char *buffer, int zero_pad )
{
	char * pbuffer = buffer;
	int	 negative = 0;
	int	 i, len, ofs;

	/* No support for unusual radixes. */
	if (radix > 16)
		return 0;

	if (value < 0 && !unsig) {
		negative = 1;
		value = -value;
	}

	/* This builds the string back to front ... */
	do {
		int digit = value % radix;
		*(pbuffer++) = (digit < 10 ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10);
		value /= radix;
	} while (value > 0);

	for (i = (pbuffer - buffer); i < zero_pad; i++)
		*(pbuffer++) = '0';

	if (negative)
		*(pbuffer++) = '-';

	*(pbuffer) = '\0';

	/* ... now we reverse it (could do it recursively but will conserve the stack space) */
	len = (pbuffer - buffer);
    ofs = len - 1;

	for (i = 0; i < ofs; i++, ofs--) {
		char tmp = buffer[i];
		buffer[i] = buffer[ofs];
		buffer[ofs] = tmp;
	}

	return len;
}


/*
虚结构类型定义, 根据场景选择不同输出类型.
*/
struct _tag_minip_context;
typedef void (* minip_puts)( struct _tag_minip_context * pctx, const char * str, int cnt );

typedef struct _tag_minip_context
{
    minip_puts pfputs;
    int tcnt;

    /* other field */
} minip_context;



int mini_vprintf( minip_context * pctx, const char * fmt, va_list vst )
{
    char  ch;
    int  cnt;
    int  zero_pad;
    const char * ptr;    
    char  bf[28];

    while( true )  {

        ptr = fmt;
        cnt = 0;
        ch = *(fmt++);

        while ( (ch != '%') && (ch != '\0') )  {
            cnt += 1;
            ch = *(fmt++);
        }

        if ( cnt > 0 )  {
            /* flush */
            pctx->pfputs( pctx, ptr, cnt );
        }

        if ( ch == '\0' )  {
            break;
        }

        /*  parse % format .. */
        ptr = bf;
        zero_pad = 0;
        ch = *(fmt++);

        if ( ch == '0' )  {

            ch = *(fmt++);

            if (ch >= '0' && ch <= '9')  {
				zero_pad = ch - '0';
                ch = *(fmt++);
            }
        }
		
		/**/
		if ( ch == 'l' || ch == 'L' || ch == 'h' || ch == 'H' )  {
			ch = *(fmt++);
		}
		
        if ( ch == '\0' )  {
            /* "%\0", nothing print. */
            break;
        }

        /**/
        switch ( ch )  {

        case 'p':
        case 'x':
            cnt = mini_itoa( va_arg(vst, unsigned int), 16, 0, 1, bf, zero_pad );
            break;

        case 'X':
            cnt = mini_itoa( va_arg(vst, unsigned int), 16, 1, 1, bf, zero_pad );
            break;
            
        case 'd':
            cnt = mini_itoa( va_arg(vst, int), 10, 0, 0, bf, zero_pad );
            break;

        case 'u':
            cnt = mini_itoa( va_arg(vst, unsigned int), 10, 0, 1, bf, zero_pad );
            break;
        
        case 'o':
            cnt = mini_itoa( va_arg(vst, unsigned int), 8, 0, 1, bf, zero_pad );
            break;

        case 'c':
            bf[0] = va_arg( vst, int );
            cnt = 1;
            break;

        case 'f':
        case 'F':
        case 'g':
        case 'G':
            /* todo, float support .. */
            va_arg(vst, double);
            bf[0] = '.';
            cnt = 1;
            break;

        case 's':
            ptr = va_arg( vst, char* );
            cnt = strlen( ptr );
            break;
		
        default:
            bf[0] = ch;
            cnt = 1;
            break;
        }

        /**/
        pctx->pfputs( pctx, ptr, cnt );
    }

    /* char number of all */
    return pctx->tcnt;
}


void dbg_stdio_puts( struct _tag_minip_context * pctx, const char * str, int cnt )
{
    for ( int i=0; i<cnt; i++ )  {
        putchar( (int)str[i] );
    }

    /**/
    pctx->tcnt += cnt;
    return;
}


int printf( const char * fmt, ... )
{
    int  iret;
    minip_context tctx;
    va_list  vst;

    /**/
    tctx.pfputs = dbg_stdio_puts;
    tctx.tcnt = 0;

    /**/
	va_start(vst, fmt);
    iret = mini_vprintf( &tctx, fmt, vst );
    va_end(vst);

    /**/
    return iret;
}


typedef struct _tag_string_context
{
    minip_puts pfputs;
    
    int tcnt;
    char * str;

} string_context;


void dbg_string_puts( struct _tag_minip_context * pctx, const char * str, int cnt )
{
    string_context * pstx;

    pstx = (string_context *)pctx;
    memcpy( &pstx->str[pstx->tcnt], str, cnt );
    pstx->tcnt += cnt;
    return;
}


int sprintf( char * str, const char * fmt, ... )
{
    int  iret;
    string_context tctx;
    va_list  vst;

    /**/
    tctx.pfputs = dbg_string_puts;
    tctx.tcnt = 0;
    tctx.str = str;

    /**/
    va_start(vst, fmt);
    iret = mini_vprintf( (minip_context *)&tctx, fmt, vst );
    va_end(vst);

    /* add tail of null byte */
    str[iret] = '\0';
    return iret;
}
